package sniffer

import (
	"encoding/binary"
	"errors"
	"fmt"
	"golang.org/x/net/ipv4"
	"net"
	"syscall"
	"unsafe"
)

const (
	// EthernetTypeLLC is not an actual ethernet type.  It is instead a
	// placeholder we use in Ethernet frames that use the 802.3 standard of
	// srcmac|dstmac|length|LLC instead of srcmac|dstmac|ethertype.
	EthernetTypeLLC                         EthernetType = 0
	EthernetTypeIPv4                        EthernetType = 0x0800
	EthernetTypeARP                         EthernetType = 0x0806
	EthernetTypeIPv6                        EthernetType = 0x86DD
	EthernetTypeCiscoDiscovery              EthernetType = 0x2000
	EthernetTypeNortelDiscovery             EthernetType = 0x01a2
	EthernetTypeTransparentEthernetBridging EthernetType = 0x6558
	EthernetTypeDot1Q                       EthernetType = 0x8100
	EthernetTypePPP                         EthernetType = 0x880b
	EthernetTypePPPoEDiscovery              EthernetType = 0x8863
	EthernetTypePPPoESession                EthernetType = 0x8864
	EthernetTypeMPLSUnicast                 EthernetType = 0x8847
	EthernetTypeMPLSMulticast               EthernetType = 0x8848
	EthernetTypeEAPOL                       EthernetType = 0x888e
	EthernetTypeERSPAN                      EthernetType = 0x88be
	EthernetTypeQinQ                        EthernetType = 0x88a8
	EthernetTypeLinkLayerDiscovery          EthernetType = 0x88cc
	EthernetTypeEthernetCTP                 EthernetType = 0x9000
)

type EthernetType uint16

// Ethernet is the layer for Ethernet frame headers.
type Ethernet struct {
	SrcMAC, DstMAC net.HardwareAddr
	EthernetType   EthernetType
	// Length is only set if a length field exists within this header.  Ethernet
	// headers follow two different standards, one that uses an EthernetType, the
	// other which defines a length the follows with a LLC header (802.3).  If the
	// former is the case, we set EthernetType and Length stays 0.  In the latter
	// case, we set Length and EthernetType = EthernetTypeLLC.
	Length uint16
}

func (eth *Ethernet) DecodeFromBytes(dataLayer []byte) error {
	if len(dataLayer) < 14 {
		return errors.New("Ethernet packet too small")
	}
	eth.DstMAC = net.HardwareAddr(dataLayer[0:6])
	eth.SrcMAC = net.HardwareAddr(dataLayer[6:12])
	eth.EthernetType = EthernetType(binary.BigEndian.Uint16(dataLayer[12:14]))
	// eth.BaseLayer = BaseLayer{data[:14], data[14:]}
	eth.Length = 0
	if eth.EthernetType < 0x0600 {
		eth.Length = uint16(eth.EthernetType)
		eth.EthernetType = EthernetTypeLLC

		//	fmt.Println(eth)
	}
	return nil
}

func main() {
	//ifi, err := net.InterfaceByName("eth0")
	//if err != nil {
	//	fmt.Print("获取设备信息错误 " + err.Error())
	//	return
	//}
	// close-on-exec
	// 当fork子进程后，仍然可以使用fd。但执行exec后系统就会字段关闭子进程中的fd了
	// 用AF_INET，上层代码无法获取以太头部信息,所以要用AF_PACKET.
	sock, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW|syscall.SOCK_CLOEXEC, int(htons(syscall.ETH_P_IP))) // ETH_P_ALL
	// sock, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(htons(uint16(ifi.Index)))) // ETH_P_ALL
	if err != nil {
		fmt.Print("创建raw socket错误" + err.Error())
		return
	}

	// 开启无线循环获取报文
	for {
		buf := make([]byte, 65536)
		// 获取报文同时解析IP报文，得到IP头和IP头后面部分（传输层和应用层）
		count, _, err := syscall.Recvfrom(sock, buf, 0)
		if err != nil {
			fmt.Println(err)
			return
		}

		fmt.Printf("Total count[%d] \n ", count)

		var ethHead Ethernet
		_ = ethHead.DecodeFromBytes(buf[:14])
		//fmt.Println(sourceAddr)
		fmt.Println("ethereal header is:================ ")
		fmt.Println(ethHead)

		header, err := ipv4.ParseHeader(buf[14:34])
		if err != nil {
			fmt.Println(err)
			return
		}
		//fmt.Println(sourceAddr)
		fmt.Println("ip header is:================ ")
		fmt.Println(header)
		//fmt.Println("body is:================ ")
		//fmt.Println(string(buf[34:]))

		// 协议字段取值: https://blog.csdn.net/m0_37806112/article/details/82787332.
		if header.Protocol == 17 {
			fmt.Println("udp body is:================ ")
			fmt.Println(string(buf[42:]))
		}
		//fmt.Println("body is:================\n ")
		//fmt.Println(string(buf[:count7]))
	}

}

func htons(i uint16) uint16 {
	b := make([]byte, 2)
	binary.BigEndian.PutUint16(b, i)
	return *(*uint16)(unsafe.Pointer(&b[0]))
}
